package orm

import (
	"context"
	"fmt"
	"geektime-go-homework/web/orm/internal/errs"
)

// Selectable 是一个标记接口
// 它代表的是查找的列，或者聚合函数等
// SELECT XXX 部分
type Selectable interface {
	selectable()
}

type Selector[T any] struct {
	builder
	table       TableReference
	where       []Predicate
	columns     []Selectable
	groupByList []Column
	havingList  []Predicate
	orderByList []OrderBy
	limit       int
	offset      int
	sess        Session
}

// func (db *DB) NewSelector[T any]()*Selector[T] {
// 	return &Selector[T]{
// 		sb: &strings.Builder{},
// 		db: db,
// 	}
// }

func NewSelector[T any](sess Session) *Selector[T] {
	c := sess.getCore()
	return &Selector[T]{
		builder: builder{
			core:   c,
			quoter: c.dialect.quoter(),
		},
		sess: sess,
	}
}

// func (r *Selector[T]) Demo[S any]() (*Query, error) {
// 	panic("implement me")
// }

func (s *Selector[T]) Build() (*Query, error) {
	if s.model == nil {
		var err error
		s.model, err = s.r.Get(new(T))
		if err != nil {
			return nil, err
		}
	}

	s.sb.WriteString("SELECT ")

	if err := s.buildColumns(); err != nil {
		return nil, err
	}

	s.sb.WriteString(" FROM ")

	if err := s.buildTable(s.table); err != nil {
		return nil, err
	}
	if len(s.where) > 0 {
		s.sb.WriteString(" WHERE ")
		p := s.where[0]
		for i := 1; i < len(s.where); i++ {
			p = p.And(s.where[i])
		}
		if err := s.buildExpression(p); err != nil {
			return nil, err
		}
	}

	// 构建group by
	if groupByErr := s.buildGroupBys(); groupByErr != nil {
		return nil, groupByErr
	}

	// 构建having
	if havingErr := s.buildHaving(); havingErr != nil {
		return nil, havingErr
	}

	// 构建order by
	if orderByErr := s.buildOrderBys(); orderByErr != nil {
		return nil, orderByErr
	}

	// 构建Limit
	if limitErr := s.buildLimit(); limitErr != nil {
		return nil, limitErr
	}

	// 构建 OFFSET
	if offsetErr := s.buildOffset(); offsetErr != nil {
		return nil, offsetErr
	}
	s.sb.WriteByte(';')
	return &Query{
		SQL:  s.sb.String(),
		Args: s.args,
	}, nil
}

func (s *Selector[T]) buildTable(table TableReference) error {
	switch t := table.(type) {
	case nil:
		// 这是代表完全没有调用 FROM，也就是最普通的形态
		s.quote(s.model.TableName)
	case Table:
		// 这个地方是拿到指定的表的元数据
		m, err := s.r.Get(t.entity)
		if err != nil {
			return err
		}
		s.quote(m.TableName)
		if t.alias != "" {
			s.sb.WriteString(" AS ")
			s.quote(t.alias)
		}
	case Join:
		s.sb.WriteByte('(')
		// 构造左边
		err := s.buildTable(t.left)
		if err != nil {
			return err
		}
		s.sb.WriteByte(' ')
		s.sb.WriteString(t.typ)
		s.sb.WriteByte(' ')
		// 构造右边
		err = s.buildTable(t.right)
		if err != nil {
			return err
		}

		if len(t.using) > 0 {
			s.sb.WriteString(" USING (")
			// 拼接 USING (xx, xx)
			for i, col := range t.using {
				if i > 0 {
					s.sb.WriteByte(',')
				}
				err = s.buildColumn(Column{name: col})
				if err != nil {
					return err
				}
			}
			s.sb.WriteByte(')')
		}

		if len(t.on) > 0 {
			s.sb.WriteString(" ON ")
			p := t.on[0]
			for i := 1; i < len(t.on); i++ {
				p = p.And(t.on[i])
			}
			if err = s.buildExpression(p); err != nil {
				return err
			}
		}

		s.sb.WriteByte(')')
	default:
		return errs.NewErrUnsupportedTable(table)
	}
	return nil
}

func (s *Selector[T]) buildExpression(expr Expression) error {
	switch exp := expr.(type) {
	case nil:
	case Predicate:
		// 在这里处理 p
		// p.left 构建好
		// p.op 构建好
		// p.right 构建好
		_, ok := exp.left.(Predicate)
		if ok {
			s.sb.WriteByte('(')
		}
		if err := s.buildExpression(exp.left); err != nil {
			return err
		}
		if ok {
			s.sb.WriteByte(')')
		}

		if exp.op != "" {
			s.sb.WriteByte(' ')
			s.sb.WriteString(exp.op.String())
			s.sb.WriteByte(' ')
		}
		_, ok = exp.right.(Predicate)
		if ok {
			s.sb.WriteByte('(')
		}
		if err := s.buildExpression(exp.right); err != nil {
			return err
		}
		if ok {
			s.sb.WriteByte(')')
		}
	case Column:
		// 这种写法很隐晦
		exp.alias = ""
		return s.buildColumn(exp)
	case value:
		s.sb.WriteByte('?')
		s.addArg(exp.val)
	case RawExpr:
		s.sb.WriteByte('(')
		s.sb.WriteString(exp.raw)
		s.addArg(exp.args...)
		s.sb.WriteByte(')')
	default:
		return errs.NewErrUnsupportedExpression(expr)
	}
	return nil
}

func (s *Selector[T]) buildColumns() error {
	if len(s.columns) == 0 {
		// 没有指定列
		s.sb.WriteByte('*')
		return nil
	}

	for i, col := range s.columns {
		if i > 0 {
			s.sb.WriteByte(',')
		}
		switch c := col.(type) {
		case Column:
			err := s.buildColumn(c)
			if err != nil {
				return err
			}
		case Aggregate:
			// 聚合函数名
			s.sb.WriteString(c.fn)
			s.sb.WriteByte('(')
			err := s.buildColumn(Column{name: c.arg})
			if err != nil {
				return err
			}
			s.sb.WriteByte(')')
			// 聚合函数本身的别名
			if c.alias != "" {
				s.sb.WriteString(" AS `")
				s.sb.WriteString(c.alias)
				s.sb.WriteByte('`')
			}
		case RawExpr:
			s.sb.WriteString(c.raw)
			s.addArg(c.args...)
		}
	}

	return nil
}

func (s *Selector[T]) addArg(vals ...any) {
	if len(vals) == 0 {
		return
	}
	if s.args == nil {
		s.args = make([]any, 0, 8)
	}
	s.args = append(s.args, vals...)
}

// 这种也是可行
// r.Select("first_name,last_name")
// func (r *Selector[T]) SelectV1(cols string) *Selector[T] {
// 	return r
// }
//
// func (r *Selector[T]) Select(cols...string) *Selector[T] {
// 	r.columns = cols
// 	return r
// }

func (s *Selector[T]) Select(cols ...Selectable) *Selector[T] {
	s.columns = cols
	return s
}

func (s *Selector[T]) From(table TableReference) *Selector[T] {
	s.table = table
	return s
}

func (s *Selector[T]) Where(ps ...Predicate) *Selector[T] {
	s.where = ps
	return s
}

func (s *Selector[T]) Get(ctx context.Context) (*T, error) {
	var err error
	s.model, err = s.r.Get(new(T))
	if err != nil {
		return nil, err
	}
	// 设置Limit 1
	s.Limit(1)
	res := get[T](ctx, s.sess, s.core, &QueryContext{
		Type:    "SELECT",
		Builder: s,
		Model:   s.model,
	})
	if res.Result != nil {
		return res.Result.(*T), res.Err
	}
	return nil, res.Err
}

func (s *Selector[T]) GetMulti(ctx context.Context) ([]*T, error) {
	panic("implement me")
}

// GroupBy
//
//	@Description: 分组统计函数
//	@receiver s
//	@param col
//	@return *Selector[T]
func (s *Selector[T]) GroupBy(col ...Column) *Selector[T] {
	s.groupByList = col
	return s
}

// buildGroupBys
//
//	@Description: 构建分组统计语句
//	@receiver s
//	@return error
func (s *Selector[T]) buildGroupBys() error {
	if len(s.groupByList) == 0 {
		return nil
	}
	s.sb.WriteString(" GROUP BY ")
	for key, c := range s.groupByList {
		if key > 0 {
			s.sb.WriteByte(',')
		}
		switch table := c.table.(type) {
		case nil:
			fd, ok := s.model.FieldMap[c.name]
			// 字段不对，或者说列不对
			if !ok {
				return errs.NewErrUnknownField(c.name)
			}
			s.quote(fd.ColName)
		case Table:
			m, err := s.r.Get(table.entity)
			if err != nil {
				return err
			}
			fd, ok := m.FieldMap[c.name]
			if !ok {
				return errs.NewErrUnknownField(c.name)
			}
			if table.alias != "" {
				s.quote(table.alias)
				s.sb.WriteByte('.')
			}
			s.quote(fd.ColName)
		default:
			return errs.NewErrUnsupportedTable(table)
		}
	}
	return nil
}

// Having
//
//	@Description: 分组统计后的条件 如: having nums > 10
//	@receiver s
//	@param ps
//	@return *Selector[T]
func (s *Selector[T]) Having(ps ...Predicate) *Selector[T] {
	s.havingList = ps
	return s
}

// buildHaving
//
//	@Description: 构造having语句
//	@receiver s
//	@return error
func (s *Selector[T]) buildHaving() error {
	if len(s.havingList) == 0 {
		return nil
	}

	if len(s.groupByList) == 0 {
		return errs.ErrGroupByCanNotBeEmptyInHaving
	}

	s.sb.WriteString(" HAVING ")
	for key, predicate := range s.havingList {
		if key > 0 {
			s.sb.WriteByte(',')
		}
		if err := s.buildHavingItem(predicate); err != nil {
			return err
		}
	}
	return nil
}

func (s *Selector[T]) buildHavingItem(expr Expression) error {
	switch exp := expr.(type) {
	case nil:
	case Predicate:
		// 在这里处理 p
		// p.left 构建好
		// p.op 构建好
		// p.right 构建好
		_, ok := exp.left.(Predicate)
		if ok {
			s.sb.WriteByte('(')
		}
		if err := s.buildHavingItem(exp.left); err != nil {
			return err
		}
		if ok {
			s.sb.WriteByte(')')
		}

		if exp.op != "" {
			s.sb.WriteByte(' ')
			s.sb.WriteString(exp.op.String())
			s.sb.WriteByte(' ')
		}
		_, ok = exp.right.(Predicate)
		if ok {
			s.sb.WriteByte('(')
		}
		if err := s.buildHavingItem(exp.right); err != nil {
			return err
		}
		if ok {
			s.sb.WriteByte(')')
		}
	case Aggregate:
		// 聚合函数本身的别名
		if exp.alias != "" {
			s.quote(exp.alias)
		} else {
			// 聚合函数名
			s.sb.WriteString(exp.fn)
			s.sb.WriteByte('(')
			err := s.buildColumn(Column{name: exp.arg})
			if err != nil {
				return err
			}
			s.sb.WriteByte(')')
		}
	case Column:
		// 这种写法很隐晦
		exp.alias = ""
		return s.buildColumn(exp)
	case value:
		s.sb.WriteByte('?')
		s.addArg(exp.val)
	default:
		return errs.NewErrUnsupportedExpression(expr)
	}
	return nil
}

// OrderBy
//
//	@Description: 根据些字段排序
//	@receiver s
//	@param orderBys
//	@return *Selector[T]
func (s *Selector[T]) OrderBy(orderBys ...OrderBy) *Selector[T] {
	s.orderByList = orderBys
	return s
}

func (s *Selector[T]) buildOrderBys() error {
	if len(s.orderByList) == 0 {
		return nil
	}
	s.sb.WriteString(" ORDER BY ")
	for key, orderBy := range s.orderByList {
		if key > 0 {
			s.sb.WriteByte(',')
		}
		switch table := orderBy.col.table.(type) {
		case nil:
			fd, ok := s.model.FieldMap[orderBy.col.name]
			// 字段不对，或者说列不对
			if !ok {
				return errs.NewErrUnknownField(orderBy.col.name)
			}
			s.quote(fd.ColName)
		case Table:
			m, err := s.r.Get(table.entity)
			if err != nil {
				return err
			}
			fd, ok := m.FieldMap[orderBy.col.name]
			if !ok {
				return errs.NewErrUnknownField(orderBy.col.name)
			}
			if table.alias != "" {
				s.quote(table.alias)
				s.sb.WriteByte('.')
			}
			s.quote(fd.ColName)
		default:
			return errs.NewErrUnsupportedTable(table)
		}
		// 写入排序方式
		s.sb.WriteByte(' ')
		s.sb.WriteString(orderBy.order)
	}
	return nil
}

// Offset
//
//	@Description: 分页偏移量
//	@receiver s
//	@param offset
//	@return *Selector[T]
func (s *Selector[T]) Offset(offset int) *Selector[T] {
	s.offset = offset
	return s
}

// buildOffset
//
//	@Description: 构建偏移量语句
//	@receiver s
//	@return error
func (s *Selector[T]) buildOffset() error {
	if s.offset == 0 {
		// 不作任何操作
		return nil
	}
	s.sb.WriteString(fmt.Sprintf(" OFFSET %d", s.offset))
	return nil
}

// Limit
//
//	@Description: 限制数据条数
//	@receiver s
//	@param limit
//	@return *Selector[T]
func (s *Selector[T]) Limit(limit int) *Selector[T] {
	s.limit = limit
	return s
}

// buildLimit
//
//	@Description: 构建限制条数语句
//	@receiver s
//	@return error
func (s *Selector[T]) buildLimit() error {
	if s.limit == 0 {
		return nil
	}
	s.sb.WriteString(fmt.Sprintf(" LIMIT %d", s.limit))
	return nil
}
